package com.ipan.jfinal.tdo.transform;

import java.io.File;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;

import com.alibaba.excel.read.listener.ReadListener;
import com.ipan.jfinal.tdo.OutputKits;
import com.ipan.jfinal.tdo.config.Mp;
import com.ipan.jfinal.tdo.config.Tpl;
import com.ipan.jfinal.tdo.exception.TransformException;
import com.ipan.kits.base.ReUtil;
import com.ipan.kits.collection.ListUtil;
import com.ipan.kits.collection.MapUtil;
import com.ipan.kits.io.FileUtil;
import com.ipan.kits.text.MoreStringUtil;
import com.ipan.kits.time.DateFormatUtil;
import com.ipan.poi.easyexcel.ehcache2.SimpleReadCacheSelector2;
import com.ipan.poi.easyexcel.patch305.EasyExcel2;

/**
 * 数据转换器
 * 
 * @author iPan
 * @date 2022-01-25
 */
public class Transducer {
	
	private static Transducer _ME = new Transducer();
	
	private Transducer() {}
	
	public static Transducer me() {
		return _ME;
	}
	
	/**
	 * 读文件
	 */
	public void readFile(File fin, File fout, Tpl tpl, ReadListener<Map<Integer, String>> readListener) {
		// 读之前先对相同名称做一次清理
		FileUtil.clearFile(fout);
//		Util.clearFile(new File(fout.getAbsolutePath() + ".tmp")); // 不需要清理，创建ReadBigFileListener的时候自动会把.tmp文件内容清除；
		
		// csv文件打开后未释放的补丁
		// 若标题不在第一行，则需指定标题位置！
		int index = (tpl != null) ? tpl.getIndex() : -1;
		int headRowNumber = (index <= 0) ? 1 : index + 1; // 从1开始计数，配置是从0开始计数；
		EasyExcel2.read(fin, readListener)
			.readCacheSelector(new SimpleReadCacheSelector2())
			.sheet()
			.headRowNumber(headRowNumber)
			.doRead();
	}
	
	/**
	 * 按行转换
	 */
	public Map transform(Map rowMap, Integer rowIndex, Tpl tpl) {
		Map resultMap = MapUtil.newLinkedHashMapWithExpectedSize(rowMap.size());
		List<Mp> mappings = tpl.getMappings();
		for (int i=0, len=mappings.size(); i<len; i++) { // 遍历列
			Mp mp = mappings.get(i);
//			Integer srcVal = Integer.valueOf(mp.getSrcVal()); // 一定小于rowMap.size()，从0开始编号；
			String srcVal = mp.getSrcVal(); // 文件是数字0、1、2的字符串，json格式是字母的字符串；
			String format = mp.getFormat();
			String targetValue = null;
			if (tpl.getKeyType() == 1 && srcVal != null && Integer.parseInt(srcVal) >= rowMap.size()) { // 当前文件格式与配置不符合
				throw new TransformException(rowIndex, i, "源文件格式的列与配置不符合！");
			}
			if (StringUtils.isBlank(format)) { // 不带格式转换
				if (srcVal == null) {
					targetValue = "";
				} else {
					targetValue = OutputKits.getCellValue(rowMap, mp, tpl.getKeyType());
				}
			} else { // 需要格式转换（format配了，srcVal必须设置）
				// 自己手动trim，EasyExcel的参数autoTrim对csv并没有支持；
				String cellValue = OutputKits.getCellValue(rowMap, mp, tpl.getKeyType()); // 源文件中列的值
				String formatBody = TransducerUtil.getFormatBody(format);
				if (StringUtils.isBlank(formatBody)) { // 格式不正确
					throw new TransformException(rowIndex, i, "源文件字段格式" + format + "不正确！");
				}
				
				if(TransducerUtil.isFormatPattern(format)) { // 货币、百分数、日期时间
					if (TransducerUtil.isClearMoneyPattern(formatBody)) { // 货币
						targetValue = TransducerUtil.clearMoney(cellValue);
					} else if (TransducerUtil.isClearPctPattern(formatBody)) { // 百分数
						targetValue = TransducerUtil.clearPct(cellValue);
					} else { // 日期时间
						if (StringUtils.isBlank(cellValue)) {
							targetValue = "";
						} else {
							String srcDateFormat = TransducerUtil.autoGetDateFormat(srcVal, cellValue);
							if (StringUtils.isBlank(srcDateFormat)) {
								throw new TransformException(rowIndex, i, "源文件字段" + cellValue + "无法匹配系统的日期时间格式！");
							}
							String cutCellValue = TransducerUtil.autoCutDateMsecValue(cellValue);
							try {
								Date srcDate = DateFormatUtil.parseDate(srcDateFormat, cutCellValue);
								targetValue = DateFormatUtil.formatDate(formatBody, srcDate);
							} catch (Exception e) { // 有可能是上次的格式不匹配（同一列存在不同的日期时间格式）
								srcDateFormat = TransducerUtil.autoScan(srcVal, cellValue); // 重新再扫描一次
								if (StringUtils.isBlank(srcDateFormat)) {
									throw new TransformException(rowIndex, i, "源文件字段" + cellValue + "无法匹配系统的日期时间格式！");
								}
								try {
									Date srcDate = DateFormatUtil.parseDate(srcDateFormat, cutCellValue);
									targetValue = DateFormatUtil.formatDate(formatBody, srcDate);
								} catch (ParseException e1) {
									TransformException ex = new TransformException("日期时间格式转换异常", e1);
									ex.setRowIndex(rowIndex);
									ex.setCellIndex(i);
									throw ex;
								}
							}
						}
					}
				
				} else if (TransducerUtil.isDictPattern(format)) { // 字典转换
					Map<String, String> map = TransducerUtil.formatBodyToMap(formatBody);
					if (StringUtils.isNotBlank(cellValue)) {
						targetValue = TransducerUtil.nullToEmpty(map.get(cellValue));
					}
					
				} else if (TransducerUtil.isMergePattern(format)) { // 单元格合并（仅支持简单的字符串合并，复杂的情况请使用自定义方法）
					String[] arr = formatBody.split(","); // 单元格合并的下标从1开始计算，最后支持携带分隔符，默认是空字符串；
					String separator = "";
					boolean hasSepartor = false;
					if (arr.length > 1) {
						String lastItem = arr[arr.length - 1];
						if (StringUtils.isNotBlank(lastItem)) {
							lastItem = lastItem.trim();
							if ((Pattern.matches("^'(.*)'$", lastItem))) { // 分隔符必须使用''环绕
								separator = MoreStringUtil.toStr(ReUtil.getGroup1("^'(.*)'$", lastItem), "");
								hasSepartor = true;
							}
						}
					}
					StringBuilder buf = new StringBuilder();
					for (int k=0; k<((hasSepartor)?arr.length-1:arr.length); k++) {
						if (k > 0) {
							buf.append(separator);
						}
						String itemCellValue = OutputKits.getCellValue(rowMap, arr[k].trim(), tpl.getKeyType());
						buf.append(itemCellValue);
					}
					targetValue = buf.toString();
				
				} else if (TransducerUtil.isInsertPattern(format)) { // 系统插值（无需对应列）
					targetValue = TransducerUtil.getLocalValueFillNull(formatBody);
					
				} else if (TransducerUtil.isDefinePattern(format)) { // 自定义方法（无需对应列）
					String[] arr = formatBody.split(",");
					String key = arr[0].trim();
					String params = null;
					if (arr.length > 1) {
						List<String> arrList = ListUtil.newArrayList(arr);
						arrList.remove(0);
						params = StringUtils.join(arrList, ",");
					}
					try {
						targetValue = DefineMethodRegister.me().execute(key, params, rowMap, srcVal, tpl);
					} catch(Exception e) {
						throw new TransformException(rowIndex, i, "自定义方法" + formatBody + "执行失败：" + e.getMessage());
					}
				}
			}
			
			if (tpl.getKeyType() == 1) {
				resultMap.put(i, targetValue);
			} else {
				resultMap.put(srcVal, targetValue);
			}
		} // end for
		
		return resultMap;
	}
	
}
